#include "qformwidget.h"

#include "qdesignermimedata.h"

#include "qwidgetfactory.h"
#include "qselectwidget.h"
#include "widgethost.h"
#include "qpropertylistview.h"
#include "qobjectproperty.h"
#include "qproperty.h"

#include "../../lib/shared/quixml.h"
#include "../../include/abstractcore.h"
#include "../../lib/shared/qproject.h"
#include "../../lib/shared/qobjecthost.h"

#include <QPainter>
#include <QPaintEvent>
#include <QMouseEvent>
#include <QDropEvent>
#include <QKeyEvent>
#include <QXMLStreamWriter>
#include <QMessageBox>

QFormWidget::QFormWidget(AbstractCore *core, QWidget *parent):
    QWidget(parent),
    m_rubber(new QRubberBand(QRubberBand::Rectangle,this)),
    m_selection(new Selection(this)),
    m_widgetHost(NULL),
    m_listview(NULL),
    m_core(core),
    m_click(false),
    m_dirty(false),
    m_host(new QObjectHost(this))
{
    m_rubber->hide();
    setAcceptDrops(true);
    this->setFocusPolicy(Qt::StrongFocus);

    setProperty("className","QWidget");
    setProperty("pageName","page1");
    setProperty("mousePressEvent","");
    setProperty("mouseMoveEvent","");
    setProperty("mouseRelessEvent","");
    setProperty("mouseDbClickEvent","");
    setProperty("designer_all_groups",QStringList()<<"Attribute"<<"Events");
    setProperty("designer_Attribute",QStringList()<<"size"<<"font"<<"focusPolicy"<<"cursor");
    setProperty("designer_Events",QStringList()<<"mousePressEvent"<<"mouseMoveEvent"<<"mouseRelessEvent"<<"mouseDbClickEvent");
}

void QFormWidget::paintEvent(QPaintEvent *e)
{
    QPainter p(this);

    p.setPen(QPen(QColor(128,128,128)));


    QRect re=e->rect();

    int x=re.x()/10;
    int y=re.y()/10;
    int w=re.width()/10+2;
    int h=re.height()/10+2;

    for(int i=0;i<w;i++)
    {
        for(int j=0;j<h;j++)
        {
            p.drawPoint((x+i)*10,(y+j)*10);
        }
    }
}

bool QFormWidget::handleMousePressEvent(QWidget *wid, QMouseEvent *e)
{
    if(m_selection->selectedWidgets().size()!=0)
    {
        if(m_selection->isWidgetSelected(wid))
        {
            if((e->modifiers() & Qt::ControlModifier)!=0)
            {
                if(m_selection->current()==wid)
                {
                    m_selection->setCurrent(m_selection->removeWidget(wid));
                }
                else
                {
                    m_selection->setCurrent(wid);
                }
                selectWidget(m_selection->current());
            }
        }
        else
        {
            if((e->modifiers() & Qt::ControlModifier)==0)
            {
                m_selection->clear();
            }
            selectWidget(wid);
        }

    }
    else
    {
        selectWidget(wid);
    }
    m_click=true;
    return true;
}

bool QFormWidget::handleMouseMoveEvent(QWidget *wid, QMouseEvent *e)
{
    if(m_click)
    {
        m_click=false;
        QWidgetList list=m_selection->selectedWidgets();
        if(list.size()>0)
        {
            QList<QDesignerDnDItemInterface*>   items;

            foreach(QWidget* wid,list)
            {
                items.append(new FormDnItem(wid,e->globalPos()));
                wid->hide();
                m_selection->hide(wid);
            }
            QDesignerMimeData::execDrag(items,this);
        }
    }
    return true;
}

bool QFormWidget::handleMouseRelessEvent(QWidget *wid, QMouseEvent *e)
{
    m_click=false;
    return true;
}

bool QFormWidget::handleMouseDoubleEvent(QWidget *wid, QMouseEvent *e)
{
    return true;
}

bool QFormWidget::handlePainteEvent(QWidget *wid, QPaintEvent *e)
{
    if(wid->property("needFrame").toBool())
    {
        QPainter p(wid);
        QPen pen(Qt::DotLine);
        pen.setColor(QColor(128,128,128));
        pen.setWidth(1);
        p.setPen(pen);
        p.setBrush(Qt::transparent);
        p.drawRect(wid->rect().adjusted(0,0,-1,-1));
    }
    return false;
}

void QFormWidget::mousePressEvent(QMouseEvent *e)
{
    m_clickPoint=e->pos();
    m_rubber->setGeometry(QRect(m_clickPoint,QSize()));
    m_rubber->show();
    m_selection->clear();
}

void QFormWidget::mouseMoveEvent(QMouseEvent *e)
{
    if(!m_clickPoint.isNull())
    {
        QRect re(QRect(m_clickPoint,e->pos()).normalized());
        m_rubber->setGeometry(re);
    }
}

void QFormWidget::mouseReleaseEvent(QMouseEvent *e)
{
    m_clickPoint=QPoint();
    QRect re=m_rubber->geometry();
    m_rubber->setGeometry(QRect(0,0,0,0));
    m_rubber->hide();

    foreach(QWidget* wid,m_widgets)
    {
        if(!(re & wid->geometry()).isNull())
        {
            m_selection->addWidget(wid);
        }
    }
    if(m_selection->selectedWidgets().size()!=0)
    {
        m_selection->setCurrent(m_selection->selectedWidgets().first());
        m_listview->setObject(m_selection->current());
        emit currentChanged(m_selection->current());
        m_widgetHost->hideHandle();
    }
    else
    {
        if(!m_widgetHost->isHandleVisible())
        {
            m_widgetHost->showHandle();
            m_listview->setObject(this);
            currentChanged(this);
        }
    }

}

void QFormWidget::keyPressEvent(QKeyEvent *e)
{
    if(e->key()==Qt::Key_Delete)
    {
        deleteSelectWidget();
    }
    else if(e->key()==Qt::Key_Left)
    {
        movePress(1);
    }
    else if(e->key()==Qt::Key_Right)
    {
        movePress(2);
    }
    QWidget::keyPressEvent(e);
}

void QFormWidget::dragEnterEvent(QDragEnterEvent *e)
{
    const QDesignerMimeData *mimeData=qobject_cast<const QDesignerMimeData*>(e->mimeData());
    if(mimeData==NULL)
    {
        return;
    }

    mimeData->acceptEvent(e);
}

void QFormWidget::dropEvent(QDropEvent *e)
{
    const QDesignerMimeData *mimeData=qobject_cast<const QDesignerMimeData*>(e->mimeData());
    if(mimeData==NULL)
    {
        return;
    }

    mimeData->acceptEvent(e);

    QList<QDesignerDnDItemInterface*>   list=mimeData->items();

    QWidget *wid;
    foreach(QDesignerDnDItemInterface *item,list)
    {
        if(item->type()==QDesignerDnDItemInterface::CopyDrop)
        {
            m_selection->clear();
            wid=addWidget(item->name(),item->ui());

            if(wid!=NULL)
            {
                QObjectProperty *obp=m_listview->getObjectProperty(wid);
                QProperty *p;
                if(item->ui()!="")
                {
                    QXmlStreamReader r(item->ui());

                    XMLWidget w;
                    while(!r.atEnd())
                    {
                        if(r.readNext()==QXmlStreamReader::StartElement)
                        {
                            w.read(r);
                            break;
                        }
                    }

                    if(!r.hasError())
                    {
                        QList<XMLProperty*> propertys=w.propertys();
                        for(int i=0;i<propertys.size();i++)
                        {
                            XMLProperty *pp=propertys.at(i);
                            wid->setProperty(pp->propertyName().toLocal8Bit(),pp->value());
                            p=obp->getProperty(pp->propertyName());
                            if(p!=NULL)
                            {
                                p->setValue(pp->value());
                                p->setNeedSave(true);
                            }
                        }
                    }
                }
                selectWidget(wid);
                wid->move(e->pos()-item->hotSpot());
                wid->show();
                p=obp->getProperty("visible");
                if(p!=NULL)
                {
                    p->setValue(true);
                    p->setModified(false);
                }
                p=obp->getProperty("objectName");
                p->setValue(getObjectName(wid));
                m_listview->getObjectProperty(wid)->updateDefaultValue();
                updateZValue();
                emit widgetAdd(wid);
            }
        }
        else
        {
            wid=item->widget();
            m_selection->show(wid);
            wid->move(e->pos()-item->hotSpot());
            wid->show();
        }
    }
}

bool QFormWidget::eventFilter(QObject *o, QEvent *e)
{
    if(o->isWidgetType())
    {
        QWidget *wid=designerWidget((QWidget*)o);
        if(wid!=NULL)
        {

            if(e->type()==QEvent::MouseButtonPress)
            {
                if(handleMousePressEvent(wid,(QMouseEvent*)e))
                {
                    return true;
                }
            }
            else if(e->type()==QEvent::MouseMove)
            {
                if(handleMouseMoveEvent(wid,(QMouseEvent*)e))
                {
                    return true;
                }
            }
            else if(e->type()==QEvent::MouseButtonRelease)
            {
                if(handleMouseRelessEvent(wid,(QMouseEvent*)e))
                {
                    return true;
                }
            }
            else if(e->type()==QEvent::MouseButtonDblClick)
            {
                if(handleMouseDoubleEvent(wid,(QMouseEvent*)e))
                {
                    return true;
                }
            }
            else if(e->type()==QEvent::Paint)
            {
                if(handlePainteEvent(wid,(QPaintEvent*)e))
                {
                    return true;
                }
            }
            else if(e->type()==QEvent::Resize || e->type()==QEvent::Move)
            {
                if(m_listview!=NULL)
                {
                    QVariant re=wid->geometry();
                    QProperty *p=m_listview->getObjectProperty(wid)->getProperty("geometry");
                    if(re!=p->value())
                    {
                        p->setValue(re);
                        p->setModified(true);
                    }
                }
            }
            else if(e->type()==QEvent::Wheel)
            {
                return true;
            }
            else if(e->type()==QEvent::ContextMenu)
            {
                return true;
            }
        }
    }
    return QWidget::eventFilter(o,e);
}

QWidget* QFormWidget::designerWidget(QWidget *widget)
{
    while(widget!=NULL)
    {
        if(widget->property("designer_widget").toBool())
        {
            return widget;
        }
        widget=widget->parentWidget();
    }
    return NULL;
}

QWidget* QFormWidget::addWidget(QString name, QString ui)
{
    QWidget *w=NULL;

    static int kk=0;
    kk++;

    w=QWidgetFactory::createWidget(name,ui);

    if(w!=NULL)
    {
        w->setParent(this);
        w->installEventFilter(this);
        const QObjectList children=w->children();

        foreach(QObject* wid,children)
        {
            wid->installEventFilter(this);
        }

        w->setFocusProxy(this);

        m_widgets.append(w);
        m_dirty=true;
        emit changed();
        emit widgetAdd(w);
        connect(w,SIGNAL(zValueChanged()),this,SLOT(updateZValue()));
    }
    return w;
}

void QFormWidget::selectWidget(QWidget *wid)
{
    if(wid==NULL)
    {
        return;
    }
    m_selection->addWidget(wid);
    m_selection->setCurrent(wid);
    m_widgetHost->hideHandle();
    m_listview->setObject(wid);
    emit currentChanged(wid);
}

void QFormWidget::deleteSelectWidget()
{
    QWidgetList list=m_selection->selectedWidgets();
    foreach(QWidget* wid,list)
    {
        emit widgetRemove(wid);
        m_widgets.removeAll(wid);
        m_selection->removeWidget(wid);
        delete wid;
        tagPageInfo info;
        info.m_data=saveBuffer();
        info.m_name=m_name;
        QProject *p=QProject::project();
        for(int i=0;i<p->getPageCount();i++)
        {
            if(p->getPage(i).m_name==m_name)
            {
                p->setPage(i,info);
                break;
            }
        }
    }
    if(!m_widgetHost->isHandleVisible())
    {
        m_widgetHost->showHandle();
        m_listview->setObject(this);
        emit currentChanged(this);
    }
}

void QFormWidget::setWidgetHost(WidgetHost *host)
{
    m_widgetHost=host;
}

WidgetHost *QFormWidget::widgetHost()
{
    return m_widgetHost;
}

void QFormWidget::setListView(QPropertyListView *listview)
{
    m_listview=listview;
    m_listview->setObject(this);
    emit currentChanged(this);
    connect(m_listview,SIGNAL(changed()),this,SLOT(formChanged()));
    connect(m_listview,SIGNAL(nameChanged()),this,SLOT(objectNameChanged()));
}

void QFormWidget::resizeEvent(QResizeEvent *)
{
    if(m_listview!=NULL)
    {
        m_listview->updateProperty("size");
    }
    if(m_widgetHost!=NULL)
    {
        m_widgetHost->updateFormGeometry();
    }
}

QString QFormWidget::saveBuffer()
{
    QString ret;

    QXmlStreamWriter w(&ret);
    QList<QProperty*>   list;

    list=m_listview->getObjectProperty(this)->propertys();

    XMLWidget xmlwidget;
    XMLProperty *p1,*p2;


    xmlwidget.setAttribute("class","QWidget");
    foreach(QProperty* p,list)
    {
        if(p->parent()==NULL && (p->modified() || p->needSave() || p->name()=="size" || p->name()=="pageName"))
        {
            p1=p->xmlProperty();
            if(p1->propertyName()!="")
            {
                p2=xmlwidget.addProperty();
                p2->setPropertyName(p1->propertyName());
                p2->setType(p1->type());
                p2->setValue(this->property(p1->propertyName().toLocal8Bit()));
            }
        }
    }

    XMLWidget *child;
    QString className;
    foreach(QWidget* wid,m_widgets)
    {
        child=new XMLWidget(&xmlwidget);
        className=wid->property("className").toString();
        if(className=="")
        {
            className=wid->metaObject()->className();
        }
        child->setAttribute("class",className);
        list=m_listview->getObjectProperty(wid)->propertys();
        foreach(QProperty* p,list)
        {

            if(p->parent()==NULL
                    && (p->modified() || p->needSave() || p->name()=="geometry" || p->name()=="objectName"))
            {
                p1=p->xmlProperty();

                if(p1->propertyName()!="")
                {
                    p2=child->addProperty();
                    p2->setPropertyName(p1->propertyName());
                    p2->setType(p1->type());
                    p2->setValue(wid->property(p1->propertyName().toLocal8Bit()));
                }
            }
        }

    }

    w.setAutoFormatting(true);
    w.writeStartDocument();

    xmlwidget.write(w);
    w.writeEndDocument();
    return ret;
}

bool QFormWidget::load(const QString &buffer)
{
    qDeleteAll(m_widgets);
    m_widgets.clear();
    XMLWidget   xml;
    QXmlStreamReader r(buffer);
    while(!r.atEnd())
    {
        if(r.readNext()==QXmlStreamReader::StartElement)
        {
            if(r.name()=="widget")
            {
                xml.read(r);
            }
        }
    }
    if(r.hasError())
    {
        return false;
    }

    QList<XMLProperty*> list;

    list=xml.propertys();

    QObjectProperty *objPro=m_listview->getObjectProperty(this);
    QProperty       *pro;
    foreach(XMLProperty* p,list)
    {
        pro=objPro->getProperty(p->propertyName());
        if(pro!=NULL)
        {
            pro->setValue(p->value());
            pro->setNeedSave(true);
        }
    }
    objPro->updateDefaultValue();
    QList<XMLWidget*>   widList;
    widList=xml.children();
    QWidget* wid;
    QString cla;
    foreach(XMLWidget* w,widList)
    {
        cla=w->attribute("class","");
        if(cla!="")
        {
            wid=addWidget(cla,"");
            if(wid==NULL)
            {
                continue;
            }
            list=w->propertys();
            objPro=m_listview->getObjectProperty(wid);
            pro=objPro->getProperty("visible");
            if(pro!=NULL)
            {
                pro->setValue(true);
                pro->setModified(false);
            }
            foreach(XMLProperty* p,list)
            {
                pro=objPro->getProperty(p->propertyName());
                if(pro!=NULL)
                {
                    pro->setValue(p->value());
                    pro->setNeedSave(true);
                    wid->setProperty(p->propertyName().toLocal8Bit(),p->value());
                }
            }

            objPro->updateDefaultValue();
        }
    }
    m_dirty=false;
    updateZValue();
    return true;
}

void QFormWidget::formChanged()
{
    QWidget* wid=(QWidget*)m_listview->object();

    while(wid!=NULL)
    {
        if(wid==this)
        {
            break;
        }
        wid=wid->parentWidget();
    }
    if(wid==NULL)
    {
        return;
    }
    m_dirty=true;
    emit changed();
}

bool QFormWidget::dirty()
{
    return m_dirty;
}

QString QFormWidget::name()
{
    return m_name;
}

void QFormWidget::setName(const QString &name)
{
    m_name=name;
}

void QFormWidget::setDirty(bool dirty)
{
    m_dirty=dirty;
}

void QFormWidget::selectThis()
{
    m_selection->clear();
    if(!m_widgetHost->isHandleVisible())
    {
        m_widgetHost->showHandle();
        m_listview->setObject(this);
        emit currentChanged(this);
    }
}

void QFormWidget::alignLeft()
{
    QWidgetList list=m_selection->selectedWidgets();
    if(list.size()<2)
    {
        return;
    }

    QWidget *curr=m_selection->current();

    if(curr==NULL)
    {
        return;
    }

    QPoint pt;

    foreach(QWidget* wid,list)
    {
        if(curr!=wid)
        {
            pt=QPoint(curr->x(),wid->y());
            if(pt!=wid->pos())
            {
                wid->move(pt);
            }
        }
    }
}

void QFormWidget::alignRight()
{
    QWidgetList list=m_selection->selectedWidgets();
    if(list.size()<2)
    {
        return;
    }

    QWidget *curr=m_selection->current();

    if(curr==NULL)
    {
        return;
    }
    QPoint pt;

    foreach(QWidget* wid,list)
    {
        if(curr!=wid)
        {
            pt=QPoint(curr->x()+curr->width()-wid->width(),wid->y());
            if(pt!=wid->pos())
            {
                wid->move(pt);
            }
        }
    }
}

void QFormWidget::alignTop()
{
    QWidgetList list=m_selection->selectedWidgets();
    if(list.size()<2)
    {
        return;
    }

    QWidget *curr=m_selection->current();

    if(curr==NULL)
    {
        return;
    }
    QPoint pt;

    foreach(QWidget* wid,list)
    {
        if(curr!=wid)
        {
            pt=QPoint(wid->x(),curr->y());
            if(pt!=wid->pos())
            {
                wid->move(pt);
            }
        }
    }
}

void QFormWidget::alignBottom()
{
    QWidgetList list=m_selection->selectedWidgets();
    if(list.size()<2)
    {
        return;
    }

    QWidget *curr=m_selection->current();

    if(curr==NULL)
    {
        return;
    }
    QPoint pt;

    foreach(QWidget* wid,list)
    {
        if(curr!=wid)
        {
            pt=QPoint(wid->x(),curr->y()+curr->height()-wid->height());
            if(pt!=wid->pos())
            {
                wid->move(pt);
            }
        }
    }
}

void QFormWidget::alignHCenter()
{
    QWidgetList list=m_selection->selectedWidgets();
    if(list.size()<2)
    {
        return;
    }

    QWidget *curr=m_selection->current();

    if(curr==NULL)
    {
        return;
    }
    QPoint pt=curr->geometry().center();
    QPoint ppt;

    foreach(QWidget* wid,list)
    {
        if(curr!=wid)
        {
            ppt=QPoint(wid->x(),pt.y()-wid->height()/2);
            if(ppt!=wid->pos())
            {
                wid->move(ppt);
            }
        }
    }
}

void QFormWidget::alignVCenter()
{
    QWidgetList list=m_selection->selectedWidgets();
    if(list.size()<2)
    {
        return;
    }

    QWidget *curr=m_selection->current();

    if(curr==NULL)
    {
        return;
    }
    QPoint pt=curr->geometry().center();
    QPoint ppt;

    foreach(QWidget* wid,list)
    {
        if(curr!=wid)
        {
            ppt=QPoint(pt.x()-wid->width()/2,wid->y());
            if(ppt!=wid->pos())
            {
                wid->move(ppt);
            }
        }
    }
}

QWidgetList QFormWidget::widgets()
{
    return m_widgets;
}

void QFormWidget::select(QWidget *wid)
{
    if(wid==NULL)
    {
        return;
    }
    if(wid==this)
    {
        m_selection->clear();
        if(!m_widgetHost->isHandleVisible())
        {
            m_widgetHost->showHandle();
            m_listview->setObject(this);
        }
    }
    else
    {
        if(m_selection->current()!=wid)
        {
            m_selection->clear();
            m_selection->addWidget(wid);
            m_selection->setCurrent(wid);
            m_widgetHost->hideHandle();
            m_listview->setObject(wid);
        }
    }
}

void QFormWidget::objectNameChanged()
{
    emit nameChanged((QWidget*)m_listview->object());
}

void QFormWidget::movePress(int type)
{
    if(m_widgets.size()==0)
    {
        return;
    }
    QWidgetList list=m_selection->selectedWidgets();

    QWidget* wid=NULL;

    if(list.size()==0)
    {
        if(type==1)
        {
            wid=m_widgets.first();
        }
        else
        {
            wid=m_widgets.last();
        }
    }
    else if(list.size()==1)
    {
        int index=m_widgets.indexOf(list.first());
        if(type==1)
        {
            if(index<m_widgets.size()-1)
            {
                wid=m_widgets.at(index+1);
            }
            else
            {
                wid=this;
            }
        }
        else
        {
            if(index>0)
            {
                wid=m_widgets.at(index-1);
            }
            else
            {
                wid=this;
            }
        }
    }
    else
    {
        int index=list.indexOf(m_selection->current());
        if(type==1)
        {
            if(index<0 || index>=list.size()-1)
            {
                wid=list.first();
            }
            else
            {
                wid=list.at(index+1);
            }
        }
        else
        {
            if(index<=0 || index>=list.size())
            {
                wid=list.last();
            }
            else
            {
                wid=list.at(index-1);
            }
        }
    }
    if(list.size()<=1)
    {
        m_selection->clear();
        if(wid==this)
        {
            selectThis();
        }
        else
        {
            selectWidget(wid);
        }
    }
    else
    {
        m_selection->setCurrent(wid);
    }

}

void QFormWidget::updateZValue()
{
    QWidgetList list;
    int zValue;
    int index;
    foreach(QWidget* wid,m_widgets)
    {
        zValue=wid->property("Z-Value").toInt();
        for(index=0;index<list.size();index++)
        {
            if(list.at(index)->property("Z-Value").toInt()>zValue)
            {
                list.insert(index,wid);
                break;
            }
        }
        if(index==list.size())
        {
            list.append(wid);
        }
    }

    foreach(QWidget* wid,list)
    {
        wid->raise();
    }
}

void QFormWidget::updatePropertyAttribute()
{
    m_host->updatePropertyAttribute();
}

QString QFormWidget::getObjectName(QWidget *wid)
{
    if(wid==NULL)
    {
        return "";
    }
    QString objectName=wid->property("objectName").toString();
    QStringList names;
    foreach(QWidget* w,m_widgets)
    {
        if(w==wid)
        {
            continue;
        }
        names.append(w->property("objectName").toString());
    }

    QString temp;
    int index=1;
    temp=objectName;
    while(names.indexOf(temp)>=0)
    {
        temp=objectName+QString("_%1").arg(index);
        index++;
    }
    return temp;
}
